#ifndef __SHM_HPP
#define __SHM_HPP

#include <iostream>
#include <string>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <unistd.h>

const std::string gpathname = "/home/cc/111/lesson26/shm";
const int gproj_id = 0x66;
const int gCreate = 1;
const int gUser = 2;
const int gShmSize = 4096;

class Shm
{
private:
    key_t GetCommonKey()
    {
        key_t key = ftok(_pathname.c_str(), _proj_id);
        if (key < 0)
        {
            perror("ftok ");
            return -1;
        }
        return key;
    }

    int GetShm(key_t key, size_t size, int flag)
    {
        int shmid = shmget(key, size, flag);
        if (shmid < 0)
        {
            perror("ftok ");
            return -1;
        }
        return shmid;
    }

    // 挂接共享内存
    void *AttachShm()
    {
        if (_addrshm != nullptr)
            DeTachShm(_addrshm);

        void *shmaddr = shmat(_shmid, nullptr, 0);
        if (shmaddr == nullptr)
        {
            perror("shmat: ");
        }

        std::cout << "who: " << RoleToString(_who) << " attach shm...." << std::endl;
        return shmaddr;
    }

    // 去关联
    void DeTachShm(void *shmaddr)
    {
        if (shmaddr == nullptr)
            return;
        shmdt(shmaddr);

        std::cout << "who: " << RoleToString(_who) << " detach shm...." << std::endl;
    }

public:
    Shm(std::string pathname, int proj_id, int who)
        : _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr)
    {
        _key = GetCommonKey();

        if (_who == gCreate)
            GetShmUseCreate();
        else if (_who == gUser)
            GetShmTOUser();

        // 不管是哪种身份，构造时直接挂接
        _addrshm = AttachShm();

        std::cout << "_shmid: " << _shmid << std::endl;
        std::cout << "_key: " << ToHex(_key) << std::endl;
    }

    ~Shm()
    {
        if (_who == gCreate)
        {
            int res = shmctl(_shmid, IPC_RMID, nullptr);
        }
        std::cout << "shm remove done...." << std::endl;
    }

    bool GetShmUseCreate()
    {
        if (_who == gCreate)
        {
            _shmid = GetShm(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);
            //sleep(5);

            if (_shmid >= 0)
                return true;

            std::cout << "shm create done...." << std::endl;
        }
        return false;
    }

    bool GetShmTOUser()
    {
        if (_who == gUser)
        {
            _shmid = GetShm(_key, gShmSize, IPC_CREAT | 0666);
            if (_shmid >= 0)
                return true;

            std::cout << "shm get done...." << std::endl;
        }
        return false;
    }

    // 再转成16进制
    std::string ToHex(key_t key)
    {
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "0x%x", key);
        return buffer;
    }

    std::string RoleToString(int who)
    {
        if (who == gCreate)
            return "Creator";
        else if (who == gUser)
            return "User";
        else
            return "None!";
    }

    void *Addr()
    {
        return _addrshm;
    }

    void Zero()
    {
        memset(_addrshm, 0, gShmSize);
    }

private:
    key_t _key;
    int _shmid;

    std::string _pathname;
    int _proj_id;

    int _who;
    void *_addrshm;
};

#endif